#ifndef __VECTOR3_H__
#define __VECTOR3_H__

#include "Evaluation.h"

namespace Nezha
{

	class NEZHA_ENGINE_ENTRY Vector3
	{
	public:
		Vector3(){}
		Vector3 (Real fX, Real fY, Real fZ)
			:x(fX), y(fY), z(fZ)
		{
		}

		Vector3(const Vector3 &v)
			:x(v.x), y(v.y), z(v.z)
		{
		}

		const Real& operator[] (int i) const
		{
			const Real *p = &x;
			return (Real&) p[i];
		}

		Real& operator[] (int i)
		{
			const Real *p = &x;
			return (Real&) p[i];
		}

		bool operator== (const Vector3& pt) const
		{
			return x == pt.x && y == pt.y && z == pt.z;
		}

		bool operator!= (const Vector3& pt) const
		{
			return !(*this == pt);
		}

		Vector3 operator+ (const Vector3& pt) const
		{
			return Vector3(x+pt.x,
				y+pt.y, z+pt.z);
		}

		Vector3 operator- (const Vector3& pt) const
		{
			return Vector3(x-pt.x,
				y-pt.y, z-pt.z);
		}

		Vector3 operator* (const Vector3& pt) const
		{
			return Vector3(x*pt.x, y*pt.y, z*pt.z);
		}

		Vector3 operator* (Real scalar) const
		{
			return Vector3(x*scalar, y*scalar, z*scalar);
		}

		Vector3 operator/ (Real scalar) const
		{
			Real inv = (Real)1.0 / scalar;
			return Vector3(x*inv, y*inv, z*inv);
		}

		Vector3 operator / (const Vector3& rhs) const
		{
			return Vector3(x/rhs.x, y/rhs.y, z/rhs.z);
		}

		Vector3 operator- () const
		{
			return Vector3(-x, -y, -z);
		}

		friend Vector3 operator* (Real scalar, const Vector3& pt);

		static Vector3 componentProduct(const Vector3& p0, const Vector3& p1)
		{
			return Vector3(p0.x*p1.x, p0.y*p1.y, p0.z*p1.z);
		}

		Vector3& operator+= (const Vector3& pt)
		{
			x += pt.x;
			y += pt.y;
			z += pt.z;
			return *this;
		}

		Vector3& operator-= (const Vector3& pt)
		{
			x -= pt.x;
			y -= pt.y;
			z -= pt.z;
			return *this;
		}

		Vector3& operator*= (Real scalar)
		{
			x *= scalar;
			y *= scalar;
			z *= scalar;
			return *this;
		}

		Vector3& operator/= (Real scalar)
		{
			Real fInvScalar = 1.0f/scalar;
			x *= fInvScalar;
			y *= fInvScalar;
			z *= fInvScalar;
			return *this;
		}

		Real length() const
		{
			return Sqrt(x*x + y*y + z*z);
		}

		Real sqrLength() const
		{
			return x*x + y*y + z*z;
		}

        bool isZeroLength(void) const
        {
            Real sqlen = (x * x) + (y * y) + (z * z);
            return (sqlen < (1e-06 * 1e-06));
        }

		Real dot(const Vector3& pt) const
		{
			return x*pt.x + y*pt.y + z*pt.z;
		}

		Real normalize()
		{
			Real len = length();

			if(len > NZ_TOLERANCE)
			{
				Real inv = 1.0f/len;
				x *= inv;
				y *= inv;
				z *= inv;
			}
			else
			{
				x = 0.0f;
				y = 0.0f;
				z = 0.0f;
				len = 0.0f;
			}

			return len;		
		}

		Vector3 cross(const Vector3& pt) const
		{
			return Vector3(y*pt.z-z*pt.y,
				z*pt.x-x*pt.z, x*pt.y-y*pt.x);		
		}

		Vector3 unitCross(const Vector3& pt) const
		{
			Vector3 re(y*pt.z-z*pt.y, z*pt.x-x*pt.z, x*pt.y-y*pt.x);
			Real len = re.length();
			if(len > NZ_TOLERANCE)
			{
				return re / len;
			}

			return Vector3::ZERO;
		}

		// This finds one of the infinite set of perpendicular vectors
		Vector3 perpendicular() const
		{
			if(fabs(x) < fabs(y))
			{
				if(fabs(z) < fabs(x))
				{
					return unitCross(Vector3::UNIT_Z);
				}
				else
				{
					return unitCross(Vector3::UNIT_X);
				}
			}
			else
			{
				if(fabs(z) < fabs(y))
				{
					return unitCross(Vector3::UNIT_Z);
				}
			}

			return unitCross(Vector3::UNIT_Y);
		}

		void set(float _x, float _y, float _z)
		{
			x = _x;
			y = _y;
			z = _z;
		}

		Real x, y, z;

	public:
		static void FastNormalize(Vector3 &v)
		{
			Real len = FastEval::FastInvSqrt(v.x*v.x + v.y*v.y + v.z*v.z);
			v.x *= len;
			v.y *= len;
			v.z *= len;
		}

		static void FastNormalizes(Vector3 *p, u32 numVectors);
		static void orthonormalize(Vector3 &U, Vector3 &V, Vector3 &W);

		static const Vector3 ZERO;
		static const Vector3 UNIT_X;
		static const Vector3 UNIT_Y;
		static const Vector3 UNIT_Z;
		static const Vector3 UNIT_ALL;
	};

	inline Vector3 operator* (Real scalar, const Vector3& pt)
	{
		return Vector3(pt.x*scalar, pt.y*scalar, pt.z*scalar);
	}

}//end namespace Nezha

#endif //end __VECTOR3_H__